package org.ns.summer.beans;

import org.ns.summer.beans.anns.Autowired;
import org.ns.summer.beans.anns.Qualifier;
import org.ns.summer.beans.definition.BeanDefinition;
import org.ns.summer.beans.definition.PropertyDefinition;
import org.ns.summer.beans.error.BeansException;
import org.ns.summer.core.AnnotationUtils;
import org.ns.summer.core.BeanUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class DefaultListableBeanFactory implements ConfigurableBeanFactory, BeanDefinitionRegistry, BeanFactory {
    private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();
    // 预定义bean
    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    private Map<Class<?>, Set<String>> beanTypeNameMap = new ConcurrentHashMap<>();
    // 单实例
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
    // 临时存放 bean
    private Map<String, Object> tempSingletonObjects = new ConcurrentHashMap<>();

    private ClassLoader beanClassLoader;
    @Override
    public void setBeanClassLoader(ClassLoader beanClassLoader) {
        this.beanClassLoader = beanClassLoader;
    }

    @Override
    public ClassLoader getBeanClassLoader() {
        return beanClassLoader;
    }

    @Override
    public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        this.beanPostProcessors.remove(beanPostProcessor);
        this.beanPostProcessors.add(beanPostProcessor);
    }

    @Override
    public int getBeanPostProcessorCount() {
        return this.beanPostProcessors.size();
    }

    @Override
    public Object initializeBean(Object existingBean, String beanName) throws BeansException {

        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            if (beanPostProcessor instanceof InitializingBeanPostProcessor) {
                ((InitializingBeanPostProcessor) beanPostProcessor).preHandler(this, existingBean, beanName);
            }
        }

        Object wrapperBean = existingBean;
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            wrapperBean = beanPostProcessor.postProcessBeforeInitialization(wrapperBean, beanName);
        }
        // ... 其他
        if (wrapperBean instanceof  InitializingBean) {
            try {
                ((InitializingBean) wrapperBean).afterPropertiesSet();
            } catch (Exception e) {
                throw new BeansException("afterPropertiesSet: " + beanName, e);
            }
        }
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            wrapperBean = beanPostProcessor.postProcessAfterInitialization(wrapperBean, beanName);
        }
        return wrapperBean;
    }

    @Override
    public String[] getBeanNamesForType(Class<?> beanType) {
        Set<String> beanNames = this.beanTypeNameMap.get(beanType);
        if (beanNames == null) {
            return new String[0];
        }
        return beanNames.toArray(new String[beanNames.size()]);
    }

    public String getBeanNamesForTypeFirst(Class<?> beanType) {
        Set<String> beanNames = this.beanTypeNameMap.get(beanType);
        if (beanNames == null || beanNames.isEmpty()) {
            return null;
        }
        return beanNames.iterator().next();
    }
    @Override
    public void preInstantiateSingletons() throws BeansException {

        List<String> beanNames = new ArrayList<>(); // 待处理的bean
        for (String beanName : this.beanDefinitionMap.keySet()) {
            if (!containsSingleton(beanName)) {
                beanNames.add(beanName);
            }
        }
        // 直接创建 bean 实例
        for (String beanName : beanNames) {
            getBean(beanName);
        }
    }

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeansException {
        if (this.beanDefinitionMap.containsKey(beanName)) { // 暂时不支持bean更新
            throw new BeansException("不支持重复bean 注册");
        }

        this.beanDefinitionMap.put(beanName, beanDefinition);
        Class<?> beanType = beanDefinition.getBeanClass();

        List<Class<?>> beanTypes = BeanUtils.getAllClass(beanType, (type)->{
            return type == java.io.Serializable.class;
        });

        for (Class<?> beanClass : beanTypes) {
            Set<String> beanNames = beanTypeNameMap.get(beanClass);
            if (beanNames == null) {
                synchronized (beanDefinitionMap) {
                    beanNames = beanTypeNameMap.get(beanClass);
                    if (beanNames == null) {
                        beanNames = new LinkedHashSet<>();
                        beanTypeNameMap.put(beanClass, beanNames);
                    }
                }
            }
            beanNames.add(beanName);
        }

    }

    @Override
    public Object getBean(String name) throws BeansException {
        return doGetBean(name);
    }

    @Override
    public boolean containsBean(String name) {
        return this.beanDefinitionMap.containsKey(name);
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return (T)getBean(name);
    }

    public boolean containsSingleton(String beanName) {
        return this.singletonObjects.containsKey(beanName);
    }

    @Override
    public BeanDefinition getBeanDefinition(String beanName) {
        if (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
            return this.beanDefinitionMap.get(beanName.substring(1));
        }
        return this.beanDefinitionMap.get(beanName);
    }

    private Object doGetBean(String beanName) {
        if (beanName == null) {
            return null;
        }

        if (containsSingleton(beanName)) {
            return this.singletonObjects.get(beanName);
        }
        Object instance = null;
        BeanDefinition beanDefinition = null;
        synchronized (singletonObjects) {
            instance = this.singletonObjects.get(beanName);
            if (instance != null) {
                return instance;
            }
            beanDefinition = this.beanDefinitionMap.get(beanName);
            if (beanDefinition == null) {
                throw new BeansException("bean不存在:" + beanName);
            }

            instance = this.tempSingletonObjects.get(beanName);
            if (instance == null) {
                instance = newInstance(beanDefinition);
            } else {
                return instance;
            }
        }

        // InstantiationAwareBeanPostProcessor 省略这一步 直接使用 BeanPostProcessor实现
        if (instance instanceof FactoryBean) { // 如果是工厂 bean

            FactoryBean factoryInstance = (FactoryBean)instance;
            this.tempSingletonObjects.put(FACTORY_BEAN_PREFIX + beanName, factoryInstance);
            propertyInjector(beanDefinition, instance);

            Object wapperObject = factoryInstance.getObject();
            this.tempSingletonObjects.put(beanName, wapperObject);
            propertyInjector(beanDefinition, wapperObject);

            factoryInstance = (FactoryBean)initializeBean(factoryInstance, FACTORY_BEAN_PREFIX + beanName);
            regiterSingletonObject(FACTORY_BEAN_PREFIX + beanName, factoryInstance);

            wapperObject = initializeBean(wapperObject, beanName);
            regiterSingletonObject(beanName, wapperObject);

            instance = wapperObject;
        } else {
            this.tempSingletonObjects.put(beanName, instance);
            propertyInjector(beanDefinition, instance);
            instance = initializeBean(instance, beanName);
            regiterSingletonObject(beanName, instance);
        }


        return instance;


    }

    private void regiterSingletonObject(String beanName, Object bean) {
        this.tempSingletonObjects.remove(beanName);
        this.singletonObjects.put(beanName, bean);
    }
    @Override
    public <T> T getBean(Class<T> requiredType) throws BeansException {
        String beanName = getBeanNamesForTypeFirst(requiredType);
        return (T)getBean(beanName);
    }



    private Object newInstance(BeanDefinition beanDefinition) {
        try {

            Constructor<?>[] declaredConstructors = beanDefinition.getBeanClass().getDeclaredConstructors();
            Constructor<?> constructor = null;
            for (Constructor<?> declaredConstructor : declaredConstructors) {
                if (declaredConstructor.getParameterCount() == 0) {
                    constructor = declaredConstructor;
                } else if (AnnotationUtils.existAnnotation(declaredConstructor, Autowired.class)) {
                    constructor = declaredConstructor;
                    break;
                }
            }
            Object object = null;
            if (constructor == null || constructor.getParameterCount() == 0) {
                object = beanDefinition.getBeanClass().newInstance();
            } else {
                Class<?> parameterTypes[] = constructor.getParameterTypes(); // 所有参数
                Annotation[] annotations = AnnotationUtils.findParameterAnnotations(constructor, Qualifier.class);
                Object args[] = new Object[parameterTypes.length];
                for (int i = 0; i < parameterTypes.length; i++) {
                    Class<?> parameterType = parameterTypes[i];
                    Qualifier qualifier = (Qualifier) annotations[i];
                    Object value = null;
                    if (qualifier == null || qualifier.value().trim().isEmpty()) {
                        value = getBean(parameterType);
                    } else {
                        value = getBean(qualifier.value().trim(), parameterType);
                    }
                    if (value == null) {
                        value = BeanUtils.getDefaultValue(parameterType);
                    }
                    args[i] = value;
                }
                object = constructor.newInstance(args);
            }

            return object;
        } catch (Exception e) {
            throw new BeansException("newInstance: "+ beanDefinition.getBeanClassName(), e);
        }
    }

    @Override
    public String[] getBeanDefinitionNames() {
        return this.beanDefinitionMap.keySet().toArray(new String[beanDefinitionMap.size()]);
    }

    private void propertyInjector (BeanDefinition beanDefinition, Object bean) {
        List<PropertyDefinition> propertyDefinitions = beanDefinition.getPropertyDefinitions();
        for (PropertyDefinition propertyDefinition : propertyDefinitions) {
            String propertyName = propertyDefinition.getPropertyName();
            Object propertyValue = propertyDefinition.getPropertyValue();
            String propertyRef = propertyDefinition.getPropertyRef();
            if (propertyValue == null) {
                propertyValue = getBean(propertyName);
            }
            BeanUtils.setFieldValue(propertyName, bean, propertyValue);
        }
        
    }
}
